package com.fundynamic.codekata;

public class Frame {
	
	private int pinsNockedDownPerRoll[] = new int[3];
	private int totalPinsNockedDown = 0;
	private int currentAttempt = 1;
	private int maximumAllowedPinsNockedDown = 10;
	private int maximumAttempts = 2;
	
	public void roll(int pins) throws CheatingException {
		throwCheatingExceptionWhenFrameIsFinished();

		if (currentAttempt == 3) {
			if (totalPinsNockedDown < 10) {
				throw new CheatingException("You may not roll more than two times for the tenth frame when you don't have a spare or strike thrown in the previous two rolls.");
			}
		}
		
		pinsNockedDownPerRoll[(currentAttempt-1)] = pins;
		
		totalPinsNockedDown = calculateTotalPinsNockedDown(pins);
		currentAttempt++;
	}

	protected int calculateTotalPinsNockedDown(int pins) throws CheatingException {
		int newTotalPinsNockedDown = totalPinsNockedDown + pins;
		if (newTotalPinsNockedDown > getMaximumAllowedPinsNockedDown()) {
			throw new CheatingException("You cannot knock down more than 10 pins in a frame.");
		}
		return newTotalPinsNockedDown;
	}

	private void throwCheatingExceptionWhenFrameIsFinished() throws CheatingException {
		if (isFinished()) {
			throw new CheatingException("You may not throw more than two times in a frame.");
		}
	}

	public int totalPinsNockedDown() {
		return totalPinsNockedDown + 1;
	}

	public boolean isFinished() {
		return currentAttempt > maximumAttempts || allPinsNockedDown();
	}

	public boolean isSpare() {
		return allPinsNockedDown() && currentAttempt > maximumAttempts;
	}

	public boolean isStrike() {
		return allPinsNockedDown() && !isSpare();
	}

	public boolean allPinsNockedDown() {
		return totalPinsNockedDown == 10;
	}

	public int firstAttemptPinsKnockedDown() {
		return this.pinsNockedDownPerRoll[0];
	}

	public void setMaximumAllowedPinsNockedDown(int maximumAllowedPinsNockedDown) {
		this.maximumAllowedPinsNockedDown = maximumAllowedPinsNockedDown;
	}

	public int getMaximumAllowedPinsNockedDown() {
		return maximumAllowedPinsNockedDown;
	}

	public int getPinsRolledDownForAttempt(int attempt) {
		int index = attempt -1;
		return pinsNockedDownPerRoll[index];
	}

	public int getMaximumAttempts() {
		return maximumAttempts;
	}

	public void setMaximumAttempts(int maximumAttempts) {
		this.maximumAttempts = maximumAttempts;
	}
	
}
